using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

namespace AGame.TabOrm
{
    public class TabOrmObjConvert
    {
        private class CommonConvertClass : TabOrmObjConvert
        {
            public override void SetConvertFunc(Type type, Func<string, object> strToObj, Func<object, string> objToStr)
            {
                Debug.LogWarning("无法在通用的转换器上面追加转换器");
            }

            public override Func<string, object> GetStringToObjFunc(Type result)
            {
                return CommonConvertDataDict.TryGetValue(result, out var func) ? func.StrToObj : null;
            }

            public override Func<object, string> GetObjToStringFunc(Type result)
            {
                return CommonConvertDataDict.TryGetValue(result, out var func) ? func.ObjToStr : null;
            }
        }

        private class ConvertData
        {
            public Func<string, object> StrToObj;
            public Func<object, string> ObjToStr;
        }

        public static readonly TabOrmObjConvert CommonConvert = new CommonConvertClass();

        private static readonly Dictionary<Type, ConvertData> CommonConvertDataDict = new Dictionary<Type, ConvertData>
        {
            {typeof(string), new ConvertData {StrToObj = StringToString, ObjToStr = CommonObjToString}},
            {typeof(bool), new ConvertData {StrToObj = StringToBoolObj, ObjToStr = BoolObjToString}},
            {typeof(int), new ConvertData {StrToObj = StringToIntObj, ObjToStr = CommonObjToString}},
            {typeof(float), new ConvertData {StrToObj = StringToFloatObj, ObjToStr = CommonObjToString}},
            {typeof(double), new ConvertData {StrToObj = StringToDoubleObj, ObjToStr = CommonObjToString}},
            
            {typeof(string[]), new ConvertData {StrToObj = StringToStringArray, ObjToStr = StringEnumerableToString}},
            {typeof(List<string>), new ConvertData {StrToObj = StringToStringList, ObjToStr = StringEnumerableToString}},
            {typeof(HashSet<string>), new ConvertData {StrToObj = StringToStringSet, ObjToStr = StringEnumerableToString}},
            
            {typeof(int[]), new ConvertData {StrToObj = StringToIntArray, ObjToStr = IntEnumerableToString}},
            {typeof(List<int>), new ConvertData {StrToObj = StringToIntList, ObjToStr = IntEnumerableToString}},
            {typeof(HashSet<int>), new ConvertData {StrToObj = StringToIntSet, ObjToStr = IntEnumerableToString}},

            {typeof(float[]), new ConvertData {StrToObj = StringToFloatArray, ObjToStr = FloatEnumerableToString}},
            {typeof(List<float>), new ConvertData {StrToObj = StringToFloatList, ObjToStr = FloatEnumerableToString}},
            {typeof(HashSet<float>), new ConvertData {StrToObj = StringToFloatSet, ObjToStr = FloatEnumerableToString}},

            {typeof(double[]), new ConvertData {StrToObj = StringToDoubleArray, ObjToStr = DoubleEnumerableToString}},
            {typeof(List<double>), new ConvertData {StrToObj = StringToDoubleList, ObjToStr = DoubleEnumerableToString}},
            {typeof(HashSet<double>), new ConvertData {StrToObj = StringToDoubleSet, ObjToStr = DoubleEnumerableToString}},
            
            {typeof(bool[]), new ConvertData {StrToObj = StringToBoolArray, ObjToStr = BoolEnumerableToString}},
            {typeof(List<bool>), new ConvertData {StrToObj = StringToBoolList, ObjToStr = BoolEnumerableToString}},
            {typeof(HashSet<bool>), new ConvertData {StrToObj = StringToBoolSet, ObjToStr = BoolEnumerableToString}},
        };

        private readonly Dictionary<Type, ConvertData> CustomConvertDict = new Dictionary<Type, ConvertData>();

        public virtual void SetConvertFunc(Type type, Func<string, object> strToObj, Func<object, string> objToStr)
        {
            CustomConvertDict[type] = new ConvertData
            {
                ObjToStr = objToStr,
                StrToObj = strToObj
            };
        }

        public virtual Func<string, object> GetStringToObjFunc(Type result)
        {
            if (CustomConvertDict.ContainsKey(result))
            {
                return CustomConvertDict[result].StrToObj;
            }

            return CommonConvertDataDict.TryGetValue(result, out var func) ? func.StrToObj : null;
        }

        public virtual Func<object, string> GetObjToStringFunc(Type result)
        {
            if (CustomConvertDict.ContainsKey(result))
            {
                return CustomConvertDict[result].ObjToStr;
            }

            return CommonConvertDataDict.TryGetValue(result, out var func) ? func.ObjToStr : null;
        }

        private static string StringToString(string str)
        {
            return str;
        }

        private static bool StringToBool(string str)
        {
            return bool.TryParse(str, out var num) && num;
        }

        private static object StringToBoolObj(string str)
        {
            return StringToBool(str);
        }

        private static string BoolObjToString(object str)
        {
            return (str is bool val && val).ToString().ToUpper();
        }


        private static int StringToInt(string str)
        {
            return int.TryParse(str, out var num) ? num : 0;
        }

        private static object StringToIntObj(string str)
        {
            return StringToInt(str);
        }

        private static float StringToFloat(string str)
        {
            return float.TryParse(str, out var num) ? num : 0f;
        }

        private static object StringToFloatObj(string str)
        {
            return StringToFloat(str);
        }

        private static double StringToDouble(string str)
        {
            return double.TryParse(str, out var num) ? num : 0d;
        }

        private static object StringToDoubleObj(string str)
        {
            return StringToDouble(str);
        }

        private static string[] StringToStringArray(string str)
        {
            return string.IsNullOrEmpty(str) ? new string[0] : str.Split('|');
        }

        private static List<string> StringToStringList(string str)
        {
            return string.IsNullOrEmpty(str) ? new List<string>() : str.Split('|').ToList();
        }
        
        private static HashSet<string> StringToStringSet(string str)
        {
            var hashSet = new HashSet<string>();
            if (string.IsNullOrEmpty(str))
            {
                return hashSet;
            }
            foreach (var s in str.Split('|'))
            {
                hashSet.Add(s);
            }
            return hashSet;
        }
        
        private static int[] StringToIntArray(string str)
        {
            return string.IsNullOrEmpty(str) ? new int[0] : str.Split('|').Select(StringToInt).ToArray();
        }

        private static List<int> StringToIntList(string str)
        {
            return string.IsNullOrEmpty(str) ? new List<int>() : str.Split('|').Select(StringToInt).ToList();
        }
        
        private static HashSet<int> StringToIntSet(string str)
        {
            var hashSet = new HashSet<int>();
            if (string.IsNullOrEmpty(str))
            {
                return hashSet;
            }
            foreach (var s in str.Split('|').Select(StringToInt))
            {
                hashSet.Add(s);
            }
            return hashSet;
        }

        private static float[] StringToFloatArray(string str)
        {
            return string.IsNullOrEmpty(str) ? new float[0] : str.Split('|').Select(StringToFloat).ToArray();
        }

        private static List<float> StringToFloatList(string str)
        {
            return string.IsNullOrEmpty(str) ? new List<float>() : str.Split('|').Select(StringToFloat).ToList();
        }
        
        private static HashSet<float> StringToFloatSet(string str)
        {
            var hashSet = new HashSet<float>();
            if (string.IsNullOrEmpty(str))
            {
                return hashSet;
            }
            foreach (var s in str.Split('|').Select(StringToFloat))
            {
                hashSet.Add(s);
            }
            return hashSet;
        }

        private static double[] StringToDoubleArray(string str)
        {
            return string.IsNullOrEmpty(str) ? new double[0] : str.Split('|').Select(StringToDouble).ToArray();
        }

        private static List<double> StringToDoubleList(string str)
        {
            return string.IsNullOrEmpty(str) ? new List<double>() : str.Split('|').Select(StringToDouble).ToList();
        }

        private static HashSet<double> StringToDoubleSet(string str)
        {
            var hashSet = new HashSet<double>();
            if (string.IsNullOrEmpty(str))
            {
                return hashSet;
            }
            foreach (var s in str.Split('|').Select(StringToDouble))
            {
                hashSet.Add(s);
            }
            return hashSet;
        }

        private static bool[] StringToBoolArray(string str)
        {
            return string.IsNullOrEmpty(str) ? new bool[0] : str.Split('|').Select(StringToBool).ToArray();
        }

        private static List<bool> StringToBoolList(string str)
        {
            return string.IsNullOrEmpty(str) ? new List<bool>() : str.Split('|').Select(StringToBool).ToList();
        }
        
        private static HashSet<bool> StringToBoolSet(string str)
        {
            var hashSet = new HashSet<bool>();
            if (string.IsNullOrEmpty(str))
            {
                return hashSet;
            }
            foreach (var s in str.Split('|').Select(StringToBool))
            {
                hashSet.Add(s);
            }
            return hashSet;
        }

        private static string CommonObjToString(object obj)
        {
            return obj == null ? "" : obj.ToString();
        }

        private static string StringEnumerableToString(object obj)
        {
            return obj == null || !(obj is IEnumerable<string> enumerable) ? "" : string.Join("|", enumerable);
        }

        private static string BoolEnumerableToString(object obj)
        {
            return obj == null || !(obj is IEnumerable<bool> enumerable) ? "" : string.Join("|", enumerable
                .Select(val=> val.ToString().ToUpper()));
        }

        private static string IntEnumerableToString(object obj)
        {
            return obj == null || !(obj is IEnumerable<int> enumerable) ? "" : string.Join("|", enumerable);
        }

        private static string FloatEnumerableToString(object obj)
        {
            return obj == null || !(obj is IEnumerable<float> enumerable) ? "" : string.Join("|", enumerable);
        }

        private static string DoubleEnumerableToString(object obj)
        {
            return obj == null || !(obj is IEnumerable<double> enumerable) ? "" : string.Join("|", enumerable);
        }
    }
}